package shardkv

import (
	"time"
)
import "6.824/raft"

const PullShardTimeout = 100 * time.Millisecond

func (kv *ShardKV) appllyShardCommand(msg *raft.ApplyMsg, index int) {
	kv.logger.Printf("appllyShardCommand: 开始处理分片命令，索引=%d，消息类型=%T", index, msg.Command)
	command := msg.Command.(ShardCommand)
	switch command.Type {
	//用拉取的切片更新当前数据库
	case "updateShard":
		kv.logger.Printf("appllyShardCommand: 处理updateShard命令，分片ID=%d", command.Sid)
		kv.updateShard(&command)
	case "notifyDone":
		kv.logger.Printf("appllyShardCommand: 处理notifyDone命令，分片ID=%d", command.Sid)
		kv.notifyDone(&command)
	case "getDelete":
		kv.logger.Printf("appllyShardCommand: 处理getDelete命令，分片ID=%d，索引=%d", command.Sid, index)
		kv.handleDelete(&command, index)
	}
}

func (kv *ShardKV) HandleCheckDelete(args *ShardArgs, reply *ShardReply) {
	kv.mu.RLock()
	kv.logger.Printf("HandleCheckDelete: 收到删除分片检查请求，类型=%s，分片ID=%d，配置号=%d",
		args.Type, args.ShardId, args.ConfigNum)
	//只有leader才可以处理请求
	if _, isLeader := kv.rf.GetState(); !isLeader {
		kv.logger.Printf("HandleCheckDelete: 不是Leader，拒绝处理请求，当前配置号=%d", kv.curConfig.Num)
		reply.ConfigNum, reply.Err = kv.curConfig.Num, ErrWrongLeader
		kv.mu.RUnlock()
		return
	}

	//如果请求的配置超了，说明当前自己还没更新到下一个配置，所以不能回复,可能某一个组已经更新完了进入下一个配置了，但是另外两个组还在通讯
	if args.ConfigNum > kv.curConfig.Num {
		kv.logger.Printf("HandleCheckDelete: 请求配置号=%d 大于当前配置号=%d，返回ErrWrongGroup", args.ConfigNum, kv.curConfig.Num)
		reply.ConfigNum, reply.Err = kv.curConfig.Num, ErrWrongGroup
		kv.mu.RUnlock()
		return
	}
	//当前版本配置比请求版本更高，说明对方这是旧版本的请求，理论上来说，应该有分片交换的应该同步进入下一个版本，
	//可能对方还没交换完毕，应该是重复了，需要提示对方直接修改状态,既可以安全删除
	if args.ConfigNum < kv.curConfig.Num {
		kv.logger.Printf("HandleCheckDelete: 请求配置号=%d 小于当前配置号=%d，返回ErrDuplicate", args.ConfigNum, kv.curConfig.Num)
		reply.ConfigNum, reply.Err = kv.curConfig.Num, ErrDuplicate
		kv.mu.RUnlock()
		return
	}

	kv.logger.Printf("HandleCheckDelete: 配置号匹配=%d，处理请求类型=%s", args.ConfigNum, args.Type)

	switch args.Type {
	case "checkDelete":
		if kv.Shards[args.ShardId].State != NeedPull {
			reply.Err = OK
			kv.logger.Printf("HandleCheckDelete: 处理checkDelete请求，分片ID=%d，可以通知对方安全删除", args.ShardId)
		} else {
			reply.Err = ErrWrongGroup
			kv.logger.Printf("HandleCheckDelete: 处理checkDelete请求，分片ID=%d，尚未拉取完毕，拒绝通知删除", args.ShardId)
		}
		kv.mu.RUnlock()
		//kv.logger.Printf("HandleCheckDelete: 处理checkDelete请求，分片ID=%d", args.ShardId)
		reply.ConfigNum = kv.curConfig.Num
	}
}

func (kv *ShardKV) HandleShardRequests(args *ShardArgs, reply *ShardReply) {
	kv.mu.RLock()
	kv.logger.Printf("HandleShardRequests: 收到分片请求，类型=%s，分片ID=%d，配置号=%d",
		args.Type, args.ShardId, args.ConfigNum)
	//只有leader才可以处理请求
	if _, isLeader := kv.rf.GetState(); !isLeader {
		kv.logger.Printf("HandleShardRequests: 不是Leader，拒绝处理请求，当前配置号=%d", kv.curConfig.Num)
		reply.ConfigNum, reply.Err = kv.curConfig.Num, ErrWrongLeader
		kv.mu.RUnlock()
		return
	}

	//如果请求的配置超了，说明当前自己还没更新到下一个配置，所以不能回复,可能某一个组已经更新完了进入下一个配置了，但是另外两个组还在通讯
	if args.ConfigNum > kv.curConfig.Num {
		kv.logger.Printf("HandleShardRequests: 请求配置号=%d 大于当前配置号=%d，返回ErrDuplicate", args.ConfigNum, kv.curConfig.Num)
		reply.ConfigNum, reply.Err = kv.curConfig.Num, ErrWrongGroup
		kv.mu.RUnlock()
		return
	}
	//当前版本配置比请求版本更高，说明对方这是旧版本的请求，理论上来说，应该有分片交换的应该同步进入下一个版本，
	//可能对方还没交换完毕，应该是重复了，需要提示对方直接修改状态
	if args.ConfigNum < kv.curConfig.Num {
		kv.logger.Printf("HandleShardRequests: 请求配置号=%d 小于当前配置号=%d，返回ErrDuplicate", args.ConfigNum, kv.curConfig.Num)
		reply.ConfigNum, reply.Err = kv.curConfig.Num, ErrDuplicate
		kv.mu.RUnlock()
		return
	}

	kv.logger.Printf("HandleShardRequests: 配置号匹配=%d，处理请求类型=%s", args.ConfigNum, args.Type)

	switch args.Type {
	case "PullShard":
		reply.Err = OK
		reply.Shard.DB = make(map[string]string)
		reply.ClientCmdSeq = make(map[int64]int64)

		for key, val := range kv.Shards[args.ShardId].DB {
			reply.Shard.DB[key] = val
		}
		//拉取后应该是需要通知对方释放
		reply.Shard.State = NeedNotify
		for key, val := range kv.ClientCmdSeq {
			reply.ClientCmdSeq[key] = val
		}
		kv.logger.Printf("HandleShardRequests: 处理PullShard完成，分片ID=%d",
			args.ShardId)
		kv.mu.RUnlock()
	case "NotifyDelete":
		kv.mu.RUnlock()
		kv.logger.Printf("HandleShardRequests: 处理NotifyDelete请求，分片ID=%d", args.ShardId)
		kv.getDelete(args, reply)
	}
}
